<template>
    <div class="cesium-map">
        <earth
            :toolbox-options="{
                top: 90,
                right: 700,
            }"
            :double-click-event="false"
            @ready="onReady"></earth>
        <slot></slot>
    </div>
    <n-dropdown
        :show="showContextMenu"
        :options="contextOptions"
        placement="bottom-start"
        trigger="manual"
        :x="contextPosition.x"
        :y="contextPosition.y"
        @select="handleSelect"
        :width="120"
        @clickoutside="handleClickContextOutside">
    </n-dropdown>
</template>

<script setup lang="ts">
import { ActionIcon } from '@/common/ActionIconList'
import IconList from '@/common/IconList'
import { useCesium, useResourceHelper } from '@/hooks'
import { IKeyObject } from '@/types/keyobject'
import { objectEntries, useDebounce, useDebounceFn } from '@vueuse/core'
import { ElMessage } from 'element-plus'
import CesiumEagleView from './EagleView/CesiumEagleView.vue'
import {
    NIcon,
    type DropdownDividerOption,
    type DropdownGroupOption,
    type DropdownOption,
    type DropdownRenderOption,
} from 'naive-ui'

import {
    ref,
    watch,
    onMounted,
    onBeforeUnmount,
    Component,
    h,
    shallowRef,
    provide,
    nextTick,
} from 'vue'
import Earth from '@/components/common/Earth/earth/earth.vue'
import { Accessibility } from '@vicons/ionicons5'
import { render } from 'vue'

import { Drawer } from 'cesium-extends'
import { IDegreeWithHeight, IExtent } from '@/types/position'
import {
    provideContextViewer,
    providePinBuilder,
} from './hooks/useCesiumContext'
import { IMovement } from '@/types/utils'

interface CesiumMapProps {
    objects?: IKeyObject[]
}

const props = defineProps<CesiumMapProps>()

type RightClickType = 'edit' | 'delete' | 'timeline' | 'view' | 'split'

const emit = defineEmits<{
    'update:objects': [entity: Cesium.Entity, object?: IKeyObject]
    'entity-click': [entity: Cesium.Entity, object?: IKeyObject]
    'right-click': [
        type: RightClickType,
        entity: Cesium.Entity,
        object?: IKeyObject,
    ]
    'timeline-click': [entity?: Cesium.Entity, object?: IKeyObject]
    'delete-click': [entity?: Cesium.Entity, object?: IKeyObject]
    'edit-click': [entity?: Cesium.Entity, object?: IKeyObject]
    'view-click': [entity?: Cesium.Entity, object?: IKeyObject]
    'split-click': [entity?: Cesium.Entity, object?: IKeyObject]
    'box-end': [positions: Cesium.Cartesian3[], lngLat: IDegreeWithHeight[]]
    'viewer-change': [params: IExtent]
    ready: [void]
}>()

const { addStaticIcon, initCesium, initEarthViewer, initCustomDataSource } =
    useCesium()

const currentObject = shallowRef<IKeyObject | null>(null)

const currentEntity = shallowRef<Cesium.Entity | null>(null)

function findObject(id: string) {
    return props.objects?.find((item) => item.id === id)
}

const mapMounted = ref(false)

function onReady() {
    initMap()
}

const map = shallowRef<Cesium.Viewer>()

provide('viewerRef', map)

provideContextViewer(map)
providePinBuilder()

let dataSource: Cesium.CustomDataSource

function initMap() {
    // map.value = initCesium(mapRef.value)

    map.value = initEarthViewer()
    mapMounted.value = true
    if (!map.value) {
        return
    }

    handleInitObjects(props.objects! || [])
    dataSource = initCustomDataSource()
    map.value.dataSources.add(dataSource)
    // 设定点击事件
    addViewChangeEvent(map.value)
    try {
        if (map.value) {
            map.value.screenSpaceEventHandler.setInputAction(
                (movement: IMovement) => {
                    currentObject.value = null
                    currentEntity.value = null

                    const pickedObject = map.value!.scene.pick(
                        movement.position,
                    )

                    if (!pickedObject)
                        return console.warn('pickedObject', pickedObject)

                    const entity: Cesium.Entity = pickedObject.id

                    // handleClickContextOutside()

                    if (Cesium.defined(pickedObject) && entity) {
                        const object = findObject(entity.id)

                        if (object) {
                            currentObject.value = object
                        }

                        if (entity) {
                            currentEntity.value = entity
                        }

                        emit('entity-click', entity, object)
                    }
                },
                Cesium.ScreenSpaceEventType.LEFT_CLICK,
            )
            map.value.screenSpaceEventHandler.setInputAction(
                (movement: IMovement) => {
                    console.log('right click movement======>', movement)

                    currentObject.value = null
                    currentEntity.value = null

                    const pickedObject = map.value!.scene.pick(
                        movement.position,
                    )

                    if (!pickedObject)
                        return console.warn('pickedObject', pickedObject)

                    const entity: Cesium.Entity = pickedObject.id

                    if (Cesium.defined(pickedObject) && entity) {
                        const object = findObject(entity.id)

                        // emit('entity-click', entity, object)

                        let position = getPixelPosition(movement)

                        if (object) {
                            currentObject.value = object
                        }

                        if (entity) {
                            currentEntity.value = entity
                        }

                        if (position) {
                            showContextMenu.value = true

                            contextPosition.value = position

                            // emit('right-click', 'delete', entity, object)
                        }
                    }
                },
                Cesium.ScreenSpaceEventType.RIGHT_CLICK,
            )
        }
    } catch (error) {
        console.log('error======>', error)
    }
}

onMounted(() => {
    initMap()
})

// 视窗变化监听相关
const debounceChange = useDebounceFn(
    (extend: IExtent) => emit('viewer-change', extend),
    1000,
)
function addViewChangeEvent(viewer: Cesium.Viewer) {
    viewer.camera.changed.addEventListener(function () {
        const params = getCesiumViewerExtend(viewer)
        debounceChange(params)
    })
}
function getCesiumViewerExtend(viewer: Cesium.Viewer) {
    let params: IExtent = {
        maxx: 0,
        maxy: 0,
        minx: 0,
        miny: 0,
    }
    // computeViewRectangle,计算椭球上的近似可见矩形（返回 Rectangle），如果椭圆形根本不可见，则返回undefined。
    let extend = viewer.camera.computeViewRectangle()
    if (typeof extend === 'undefined') {
        //2D下会可能拾取不到坐标，extend返回undefined,所以做以下转换
        let canvas = viewer.scene.canvas
        let upperLeft = new Cesium.Cartesian2(0, 0) //canvas左上角坐标转2d坐标
        let lowerRight = new Cesium.Cartesian2(
            canvas.clientWidth,
            canvas.clientHeight,
        ) //canvas右下角坐标转2d坐标

        let ellipsoid = viewer.scene.globe.ellipsoid
        let upperLeft3 = viewer.camera.pickEllipsoid(upperLeft, ellipsoid) //2D转3D世界坐标

        let lowerRight3 = viewer.camera.pickEllipsoid(lowerRight, ellipsoid) //2D转3D世界坐标

        let upperLeftCartographic =
            viewer.scene.globe.ellipsoid.cartesianToCartographic(upperLeft3!) //3D世界坐标转弧度
        let lowerRightCartographic =
            viewer.scene.globe.ellipsoid.cartesianToCartographic(lowerRight3!) //3D世界坐标转弧度

        let minx = Cesium.Math.toDegrees(upperLeftCartographic.longitude) //弧度转经纬度
        let maxx = Cesium.Math.toDegrees(lowerRightCartographic.longitude) //弧度转经纬度

        let miny = Cesium.Math.toDegrees(lowerRightCartographic.latitude) //弧度转经纬度
        let maxy = Cesium.Math.toDegrees(upperLeftCartographic.latitude) //弧度转经纬度

        params.minx = minx // 最小经度
        params.maxx = maxx // 最大经度
        params.miny = miny // 最小纬度
        params.maxy = maxy // 最大纬度
    } else {
        //3D获取方式
        // 最大经度
        params.maxx = Cesium.Math.toDegrees(extend.east) //弧度转经纬度
        // 最大纬度
        params.maxy = Cesium.Math.toDegrees(extend.north) //弧度转经纬度
        // 最小经度
        params.minx = Cesium.Math.toDegrees(extend.west) //弧度转经纬度
        // 最小纬度
        params.miny = Cesium.Math.toDegrees(extend.south) //弧度转经纬度
    }
    return params //返回屏幕所在经纬度范围
}

function getPixelPosition(movement: IMovement) {
    var pixelPosition = new Cesium.Cartesian2(
        movement.position.x,
        movement.position.y,
    )

    if (map.value) {
        var pixelPositionRelativeToParent =
            map.value?.scene.canvas.getBoundingClientRect()
        pixelPositionRelativeToParent.x += pixelPosition.x
        pixelPositionRelativeToParent.y += pixelPosition.y

        return pixelPositionRelativeToParent
    } else {
        return null
    }
}

const renderIcon = (icon: Component) => {
    return () => {
        return h(NIcon, null, {
            default: () => h(icon),
        })
    }
}

const showContextMenu = ref(false)
const contextPosition = ref<DOMRect | any>({
    x: 1,
    y: 1,
})
const contextOptions = shallowRef<
    Array<
        | DropdownOption
        | DropdownGroupOption
        | DropdownDividerOption
        | DropdownRenderOption
    >
>([
    {
        label: '删除',
        key: 'delete',
        props: {
            onClick() {
                emit('delete-click', currentEntity.value!, currentObject.value!)
            },
        },
        icon: renderIcon(ActionIcon.Delete),
    },
    {
        label: '编辑',
        key: 'edit',
        props: {
            onClick() {
                emit('edit-click', currentEntity.value!, currentObject.value!)
            },
        },
        icon: renderIcon(ActionIcon.Edit),
    },
    {
        label: '查看',
        key: 'view',
        props: {
            onClick() {
                emit('view-click', currentEntity.value!, currentObject.value!)
            },
        },
        icon: renderIcon(ActionIcon.View),
    },
    // {
    //     label: '卷帘',
    //     key: 'split',
    //     props: {
    //         onClick() {
    //             emit('split-click', currentEntity.value!, currentObject.value!)
    //         },
    //     },
    //     icon: renderIcon(ActionIcon.SplitHorizon),
    // },
    // {
    //     label: '时间线',
    //     key: 'timeline',
    //     props: {
    //         onClick() {
    //             emit('split-click', currentEntity.value!, currentObject.value!)
    //         },
    //     },
    //     icon: renderIcon(ActionIcon.Clock),
    // },
])

function handleClickContextOutside() {
    // ElMessage.info('clickoutside')
    showContextMenu.value = false
}

onBeforeUnmount(() => {
    mapMounted.value = false
})

function cleanAllObjects() {
    const entityDataSource =
        map.value?.dataSources.getByName('entity-datasource')[0]

    if (entityDataSource) {
        console.log('entityDataSource======>', entityDataSource)
        entityDataSource.entities.removeAll()
    }
}

function handleSelect(key: string | number) {
    showContextMenu.value = false
    // message.info(String(key))
}

const { getStaticUrl } = useResourceHelper()

function handleInitObjects(objects: IKeyObject[]) {
    if (map.value) {
        objects.forEach((item) => {
            console.log('item======>', item)

            addStaticIcon({
                icon: getStaticUrl(item.icon),
                id: item.id,
                loc: item.loc!,
                name: item.name,
                color: Cesium.Color.BLUE,
            })
        })
    }
}

// defineExpose({
//     handleBoxSelect,
//     handleBoxClear,
// })

watch(
    () => props.objects,
    (nv) => {
        // clean all entity
        cleanAllObjects()
        // load entity icon and text

        if (nv) {
            nextTick(() => {
                console.log('objects nv======>', nv)
                handleInitObjects(nv)
            })
        }
    },
    {
        // immediate: true,
        deep: true,
        flush: 'post',
    },
)
</script>

<style lang="scss" scoped>
.cesium-map {
    height: 100%;
    width: 100%;
    #map {
        width: 100%;
        height: 100%;
    }
}
</style>
@/types/position
